package mlProject.classifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BayesClassifer {
	public static final double MIN_VALUE = 0.0000000001;
	public Map<String, Map<String, Double>> attrValueP;
	public Map<String, Map<String, Double>> attrP;
	public Map<String, Double> resP;
	public Map<String,Map<String, Double>> u;
	public Map<String,Map<String, Double>> v;
	
	public int cnt;

	public BayesClassifer() {
		attrValueP = new HashMap<>();
		attrP = new HashMap<>();
		resP = new HashMap<String, Double>();
		u = new HashMap<>();
		v = new HashMap<>();
	}
	
	
	public void calU(String label,String attr,List<DataModel> datas){
		Double sum = 0.0;
		int cnt = 0;
		for(int i = 0;i< datas.size();i++){
			if(datas.get(i).label.equals(label)){
				sum += (double)(datas.get(i).attrs.get(attr));
				cnt ++;
			}
		}
		
		Map<String,Double> tmpu;
		if(u.containsKey(label)){
			tmpu = u.get(label);
		}else{
			tmpu = new HashMap<>();
		}
		tmpu.put(attr, sum/cnt);
		u.put(label,tmpu);
	}
	
	public void calV(String label,String attr,List<DataModel> datas){
		if(!u.get(label).containsKey(attr))
			calU(label,attr, datas);
		
		Double sum = 0.0;
		int cnt = 0;
		Double tmpu = u.get(label).get(attr);
		
		for(int i =  0;i < datas.size();i++){
			if(datas.get(i).label.equals(label)){
				Double tmpv = (double)(datas.get(i).attrs.get(attr));
				sum += ((tmpv-tmpu)*(tmpv-tmpu));
			}
		}
		Double res =  Math.sqrt(sum/cnt);
		Map<String,Double> tmpv;
		if(v.containsKey(label)){
			tmpv = v.get(label);
		}else{
			tmpv = new HashMap<>();
		}
		v.put(label,tmpv);
	}
	
	public double calGuassP(double u,double v,double x){
		return 1.0/(Math.sqrt(2*Math.PI)*v)*Math.exp(-(x-u)*(x-u)/2*v*v);
	}
	
	public void calculateP(List<DataModel> datas) {
		cnt = datas.size();
		for (int i = 0; i < datas.size(); i++) {
			System.out.println(i);
			DataModel data = datas.get(i);

			Map<String, Double> tmpAttrValueP;
			Map<String, Double> tmpAttrP;

			if (!resP.containsKey(data.label)) {
				resP.put(data.label, 0.0);
				tmpAttrValueP = new HashMap<String, Double>();
				tmpAttrP = new HashMap<String, Double>();
				attrValueP.put(data.label, tmpAttrValueP);
				attrP.put(data.label, tmpAttrP);
			} else {
				tmpAttrValueP = attrValueP.get(data.label);
				tmpAttrP = attrP.get(data.label);
			}

			resP.put(data.label, resP.get(data.label) + 1);

			for (String attr : data.attrs.keySet()) {
				if (!attr.startsWith("N_")) {
					String value = (String) data.attrs.get(attr);

					StringBuffer kv = new StringBuffer();
					kv.append(attr);
					kv.append("|");
					kv.append(value);
					
					if(!tmpAttrValueP.containsKey(kv)){
						tmpAttrValueP.put(kv.toString(),1.0);
					}else{
						tmpAttrValueP.put(kv.toString(), tmpAttrValueP.get(kv.toString()) + 1);
						
					}
					
					if(!tmpAttrP.containsKey(attr)){
						tmpAttrP.put(attr,1.0);
					}else{
						tmpAttrP.put(attr, tmpAttrP.get(attr) + 1);
					}
					
				} else{
//					如果是数值型的，只需要把均值和方差预存一下
					if(!u.containsKey(data.label) || !u.get(data.label).containsKey(attr)){
						calU(data.label,attr,datas);
					}
					if(!v.containsKey(data.label) || !v.get(data.label).containsKey(attr)){
						calV(data.label,attr,datas);
					}
				}
			}

		}

		//计算一下总概率
		for (String key : resP.keySet()) {
			resP.put(key, resP.get(key) * 1.0 / cnt);
		}
		
		//针对每个属性的每个值，计算一下概率
		for (String key : attrValueP.keySet()) {
			
			Map<String, Double> tmpAttrValueP = attrValueP.get(key);
			Map<String, Double> tmpAttrP = attrP.get(key);

			for (String kv : tmpAttrValueP.keySet()) {
				int idx = kv.indexOf('|');
				String attr = kv.substring(0, idx);

				double p = tmpAttrValueP.get(kv) / tmpAttrP.get(attr);
				tmpAttrValueP.put(kv, p);
			}
		}
	}
	
	
	public String classify(DataModel data){
		Map<String,Double> res = new HashMap<>();
		for(String label : resP.keySet()){
			double p = 1.0;
			for(String attr : data.attrs.keySet()){
				if(attr.startsWith("N_")){
					double x = (double) (data.attrs.get(attr));
					p *= calGuassP(u.get(label).get(attr),v.get(label).get(attr), x);
				}else{
					StringBuffer kv = new StringBuffer();
					kv.append(attr);
					kv.append("|");
					kv.append(data.attrs.get(attr));
					if(attrValueP.get(label).containsKey(kv)){
						p *= attrValueP.get(label).get(kv);
					}else{
						p *= MIN_VALUE;
					}
				}
			}
			res.put(label,p);
		}
		
		double maxv = -1.0;
		String resLabel = "";
		
		for(String label: res.keySet()){
			if(maxv == -1.0){
				resLabel = label;
				maxv = res.get(label);
			}else{
				if(maxv < res.get(label)){
					resLabel = label;
					maxv = res.get(label);
				}
			}
		}
		return resLabel;
	}
	
	public static void main(String args[]){
//		DataModelPrepare dmp = new DataModelPrepare("bank-additional-full.csv",';');
//		List<String> attrList = dmp.getAttrList();
//		List<DataModel> data = dmp.getDataModel();
//		
//		BayesClassifer bc = new BayesClassifer();
//		bc.calculateP(data);
	}
}




























